package simulation;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Set;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.Timer;
import com.rabbitmq.client.Channel;

import behaviour.Explorer;
import behaviour.PacmanFinder;

import pacman.Game;
import pacman.Ghost;
import pacman.GhostRunner;
import pacman.Pacman;
import maze.Barcode;
import maze.Coordinate;
import maze.Direction;
import maze.Location;
import maze.Maze;

public class GuiTest extends Panel {
	private final int FPS = 30;
	private Simulator simulator; // The simulation that calculates the values.

	BufferedImage sector;
	BufferedImage pacman;
	BufferedImage background;
	BufferedImage map;
	BufferedImage colormap;
	BufferedImage robot;
	BufferedImage blackScreen;
	BufferedImage robotrotated;
	BufferedImage fog;
	BufferedImage ultrasonic;

	BufferedImage MazePanel = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
	BufferedImage MazePanelBuffer = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
	boolean drawColormap = false;
	boolean drawDiffusion = false;
	Maze maze;
	Game game;
	private int selectedghost = 1;
	private ArrayList<Ghost> ghosts;
	private ArrayList<DataCollector> ghostsData = new ArrayList<DataCollector>();
	int toDraw = 0; // 0 = real map, 1 = map from robot 1, 2 for robot 2 etc...
	int [][] lights = new int[4][50];
	boolean initiated = false;
	boolean drawing = false;
	boolean imagesloaded = false;
	private double x;
	private double y;
	private double xOffset = -0.5;
	private double yOffset = -0.5;
	JButton Rightbutton = new JButton("X Right");
	JButton Leftbutton = new JButton("X Left");
	JButton Upbutton = new JButton("Y Down");
	JButton Downbutton = new JButton("Y UP");
	Timer timer;
	ColorMap color;
	private char[][] walls = { // index with maze[-y][x]
	{ 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 },
			{ 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 2, 1 },
			{ 1, 2, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1 },
			{ 1, 2, 1, 2, 1, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1 },
			{ 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1 },
			{ 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1 },
			{ 1, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 2, 1 },
			{ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1 },
			{ 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 2, 2, 1 },
			{ 1, 2, 1, 2, 2, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
			{ 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1 },
			{ 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
			{ 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
			{ 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1 },
			{ 1, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 1 },
			{ 1, 2, 2, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1 },
			{ 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1 },
			{ 1, 2, 2, 1, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1 },
			{ 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
			{ 1, 2, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, 1, 2, 2, 2, 1, 2, 1 },
			{ 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1 } };

	public GuiTest(final Simulator simulator, Game game) {
		this.simulator = simulator;
		this.game = game;
		if (simulator.getMaze() != null) {
			walls = simulator.getMaze().getRawData();
			maze = simulator.getMaze();
		}
		if (simulator.getColorMap() != null)
			color = simulator.getColorMap();

		ghosts = new ArrayList<Ghost>();
		Set<Ghost> robs = simulator.getGhostLocations().keySet();
		for (Ghost ghost : robs) {
			if (!ghosts.contains(ghost)) {
				ghosts.add(ghost);
				DataCollector data = new DataCollector(FPS, ghost);
				ghostsData.add(data);
				data.run();
			}
		}

		// random values to test graph
		/*
		 * for (int i = 0; i < 100; i++) { light1[i] = (int) ((Math.random() *
		 * 20)); if (Math.random()<0.5) light1[i] = -light1[i]; } for (int i =
		 * 0; i < 100; i++) { light2[i] = (int) ((Math.random() * 20)); if
		 * (Math.random()<0.5) light2[i] = -light2[i]; } for (int i = 0; i <
		 * 100; i++) { light3[i] = (int) ((Math.random() * 20)); if
		 * (Math.random()<0.5) light3[i] = -light3[i]; } for (int i = 0; i <
		 * 100; i++) { light4[i] = (int) ((Math.random() * 20)); if
		 * (Math.random()<0.5) light4[i] = -light4[i]; }
		 */
		// end random values.

		setLayout(new BorderLayout());
		// mouselistener to select robots
		this.addMouseListener(new MouseListener() {

			@Override
			public void mouseClicked(MouseEvent click) {
				// System.out.println(click.getX()+ "," + click.getY());
				if (click.getX() > 6 * getWidth() / 8) {
					if (click.getY() < getHeight() / 4 && click.getY() > 0) {
						selectedghost = 0;
						toDraw = 1;
					} else if (click.getY() < getHeight() / 4 * 2 && click.getY() > getHeight() / 4) {
						selectedghost = 1;
						toDraw = 2;
					} else if (click.getY() < getHeight() / 4 * 3 && click.getY() > getHeight() / 4 * 2) {
						selectedghost = 2;
						toDraw = 3;
					} else if (click.getY() < getHeight() && click.getY() > getHeight() / 4 * 3) {
						selectedghost = 3;
						toDraw = 4;
					}

				} else
					toDraw = 0;

			}

			@Override
			public void mouseEntered(MouseEvent arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void mouseExited(MouseEvent arg0) {
				// TODO Auto-generated method stub

			}

			@Override
			public void mousePressed(MouseEvent arg0) {

			}

			@Override
			public void mouseReleased(MouseEvent arg0) {

			}
		});
		this.addMouseMotionListener(new MouseMotionListener() {
			int x = 0;
			int y = 0;
			boolean init = false;

			@Override
			public void mouseDragged(MouseEvent arg0) {
				if ((arg0.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) == MouseEvent.BUTTON1_DOWN_MASK) {
					if (init) {
						xOffset += (x - arg0.getX()) / 40.0;
						yOffset += (y - arg0.getY()) / 40.0;
					}
					x = arg0.getX();
					y = arg0.getY();
					init = true;
				}
			}

			@Override
			public void mouseMoved(MouseEvent arg0) {
				if (arg0.getButton() == MouseEvent.NOBUTTON)
					init = false;
			}

		});
		// FPS is frames per second to draw to screen;
		timer = new Timer(1000 / FPS, new ActionListener() {

			// timer to set when to draw the screen and update values
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if (initiated) {
					draw();
				}
				Set<Ghost> robs = simulator.getGhostLocations().keySet();
				for (Ghost ghost : robs) {
					if (!ghosts.contains(ghost)) {
						ghosts.add(ghost);
						DataCollector data = new DataCollector(FPS, ghost);
						ghostsData.add(data);
						data.run();
					}
				}

				// random values for lightsensor, normaly for each robot get
				// this from sensor
				
				for(int i = 0; i < ghostsData.size();i++){
					try{
						AddLightValue(lights[0], (ghostsData.get(i).getLight() - 127) * 20 / 127);
					} catch(Exception e){
						AddLightValue(lights[i], 0);
					}
				}
//				try {
//					AddLightValue(light1, (ghostsData.get(0).getLight() - 127) * 20 / 127);
//				} catch (Exception e) {
//					AddLightValue(light1, 0);
//				}
//				;
//
//				try {
//					AddLightValue(light2, (ghostsData.get(1).getLight() - 127) * 20 / 127);
//				} catch (Exception e) {
//					AddLightValue(light2, 0);
//				}
//				;
//
//				try {
//					AddLightValue(light3, (ghostsData.get(2).getLight() - 127) * 20 / 127);
//				} catch (Exception e) {
//					AddLightValue(light3, 0);
//				}
//				;
//
//				try {
//					AddLightValue(light4, (ghostsData.get(3).getLight() - 127) * 20 / 127);
//				} catch (Exception e) {
//					AddLightValue(light4, 0);
//				}
//				;

				timer.restart();

			}
		});

		timer.start();
		this.setFocusable(true);
		this.requestFocus();
		KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
		manager.addKeyEventDispatcher(new MyDispatcher());
		loadImages();
		this.setVisible(true);
		// pacman location for testing
		x = 15;
		y = 15;
		this.setPreferredSize(new Dimension(800, 600));
		this.validate();

		JFrame jf = new JFrame("Simulation");
		this.setFocusable(true);
		this.requestFocusInWindow();
		jf.setFocusable(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.getContentPane().add(this);
		jf.getContentPane().setPreferredSize(new Dimension(800, 600));
		jf.validate();
		jf.pack();
		jf.setVisible(true);

		initiated = true;

	}
	public GuiTest(final Simulator simulator){
		this(simulator, null);
	}
	/**
	 * add a new value to the history of lightvalues to draw
	 * 
	 * @param original
	 *            |Original Array of values
	 * @param newvalue
	 *            |New Value to add to the end
	 */
	private void AddLightValue(int[] original, int newvalue) {
		for (int i = 0; i < original.length - 1; i++) {
			original[i] = original[i + 1];
		}
		original[original.length - 1] = newvalue;
		// return original;
	}

	/**
	 * adds to the position of pacman for tests
	 * 
	 * @param xpos
	 * @param ypos
	 */
	public void setPosition(double xpos, double ypos) {
		x += xpos;
		y += ypos;
	}

	/**
	 * Loads the images used
	 */
	public void loadImages() {
		try {
			sector = ImageIO.read(new File("simulation/sector_rand.png"));
			pacman = ImageIO.read(new File("simulation/pacman.png"));
			background = ImageIO.read(new File("simulation/wood.jpg"));
			robot = ImageIO.read(new File("simulation/robot.png"));
			blackScreen = ImageIO.read(new File("simulation/DataScreenRobot.png"));
			robotrotated = ImageIO.read(new File("simulation/robot.png"));
			fog = ImageIO.read(new File("simulation/fogofwar.png"));
			ultrasonic = ImageIO.read(new File("simulation/ultrasonic.png"));

		} catch (IOException ex) {
			try {
				sector = ImageIO.read(new File("..\\PenOGuiFiles\\sector_rand.png"));
				pacman = ImageIO.read(new File("..\\PenOGuiFiles\\pacman.png"));
				background = ImageIO.read(new File("..\\PenOGuiFiles\\wood.jpg"));
				robot = ImageIO.read(new File("..\\PenOGuiFiles\\robot.png"));
				blackScreen = ImageIO.read(new File("..\\PenOGuiFiles\\DataScreenRobot.png"));
				robotrotated = ImageIO.read(new File("..\\PenOGuiFiles\\robot.png"));
			} catch (IOException e) {
				try {
					sector = ImageIO.read(new File("../PenOTiles/sector_rand.png"));
					pacman = ImageIO.read(new File("../PenOTiles/pacman.png"));
					background = ImageIO.read(new File("../PenOTiles/wood.jpg"));
					robot = ImageIO.read(new File("../PenOTiles/robot.png"));
					blackScreen = ImageIO.read(new File("../PenOTiles/DataScreenRobot.png"));
					robotrotated = ImageIO.read(new File("../PenOTiles/robot.png"));
					fog = ImageIO.read(new File("../PenOTiles/fogofwar.png"));
					ultrasonic = ImageIO.read(new File("../PenOTiles/ultrasonic.png"));
				} catch (IOException e1) {
					System.out.println("file not found in GuiTest");
				}

			}
		}
		imagesloaded = true;
	}

	private void drawColorMap(Graphics map) {

		BufferedImage[][] tiles = color.getTileMap();

		for (int i = 0; i < tiles.length; i++) {
			for (int j = 0; j < tiles[0].length; j++) {
				map.drawImage(tiles[j][tiles.length - 1 - i], j * 40, i * 40, null);// colormap.getHeight()-((i+1)*40),null);
			}
		}
		map.dispose();
	}

	/**
	 * Draws the whole map with walls to an image to avoid overhead work
	 * 
	 * @param map
	 */
	private void drawMazeImage(BufferedImage m, char[][] mazeToDraw, boolean drawBarcodes) {

		Graphics map = m.getGraphics();
		BufferedImage fogofwar = new BufferedImage(m.getWidth(), m.getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics fogfwar = fogofwar.getGraphics();

		// draw sectors

		for (int i = 0; i < mazeToDraw.length - 1; i++) {
			for (int j = 0; j < (mazeToDraw[1].length) / 2; j++) {
				double YStartPos = (i) * 40;
				double XStartPos = (j) * 40;
				map.drawImage(sector, (int) XStartPos, (int) YStartPos, null);
			}
		}

		// draw barcodes
		if (drawBarcodes) {
			for (Barcode code : maze.getBarcodes()) {
				addBarcode(code.getX(), code.getY(), code.getCode(), code.getDirection());
			}
		}

		// draw walls
		map.setColor(new Color(90, 43, 0));
		int YStart = 0;// (int)(-yOff*40);
		int XStart = 0;// (int)(-xOff*40);

		for (int i = 0; i < mazeToDraw.length; i++) {
			for (int h = 0; h < mazeToDraw[1].length; h += 2) {
				if (mazeToDraw[(int) (i)][(int) (h)] == 1) {

					map.drawLine(h / 2 * 40 - 1 - XStart, (i - 1) * 40 - YStart, (h / 2) * 40 - 1 - XStart, (i + 1 - 1) * 40 - YStart);
					map.drawLine(h / 2 * 40 - XStart, (i - 1) * 40 - YStart, (h / 2) * 40 - XStart, (i + 1 - 1) * 40 - YStart);
					map.drawLine(h / 2 * 40 + 1 - XStart, (i - 1) * 40 - YStart, (h / 2) * 40 + 1 - XStart, (i + 1 - 1) * 40 - YStart);
				}
				if (mazeToDraw[(int) (i)][(int) (h)] == 0) {
					map.drawImage(fog, (((h / 2) * 40) - 20), ((i - 1) * 40 - YStart), null);

				}

			}
			for (int v = 0; v < mazeToDraw[1].length - 1; v += 2) {

				if (mazeToDraw[i][v + 1] == 1) {

					map.drawLine((v / 2) * 40, (i + 1 - 1) * 40 - 1, (v / 2 + 1) * 40, (i + 1 - 1) * 40 - 1);
					map.drawLine((v / 2) * 40, (i + 1 - 1) * 40, (v / 2 + 1) * 40, (i + 1 - 1) * 40);
					map.drawLine((v / 2) * 40, (i + 1 - 1) * 40 + 1, (v / 2 + 1) * 40, (i + 1 - 1) * 40 + 1);
				}
				if (mazeToDraw[i][v + 1] == 0) {
					map.drawImage(fog, (((v / 2) * 40)), (((i) * 40) - 20), null);

				}
			}
		}
		fogfwar.dispose();
		// map.drawImage(fogofwar, 0,0, null);
		// map.drawImage(scale(scale(fogofwar,1/8),8), 0,0, null);
		map.dispose();
	}

	/**
	 * Draws the whole map with walls to an image to avoid overhead work
	 * 
	 * @param map
	 */
	private BufferedImage drawGhostMazeImage(BufferedImage m, Maze ghostMaze, boolean drawBarcodes, Maze diffusionMaze) {
		int maxX = ghostMaze.getMaxX() + 1;
		int maxY = ghostMaze.getMaxY() + 1;
		int minX = ghostMaze.getMinX();
		int minY = ghostMaze.getMinY();
		int minXWrap = ghostMaze.getMinXWrap();
		int minYWrap = ghostMaze.getMinYWrap();
		int size = ghostMaze.getSize();

		// the image to draw the whole maze onto, even the white space

		BufferedImage temp = new BufferedImage(40 * (int) (ghostMaze.getRawData()[0].length / 2) + 40, 40 * ghostMaze.getRawData().length, BufferedImage.TYPE_INT_ARGB);

		BufferedImage fogofwar = new BufferedImage(40 * (int) (ghostMaze.getRawData()[0].length / 2) + 40, 40 * ghostMaze.getRawData().length, BufferedImage.TYPE_INT_ARGB);
		Graphics2D map = (Graphics2D) temp.getGraphics();
		map.setStroke(new BasicStroke(2f));
		Graphics fogfwar = fogofwar.getGraphics();
		// System.out.println(m.getWidth() + "," + m.getHeight());
		// draw sectors

		char[][] mazeToDraw = ghostMaze.getRawData();
		for (int i = 0; i < size + 1; i++) {
			for (int j = 0; j < size + 1; j++) {
				double YStartPos = (i) * 40;
				double XStartPos = (j) * 40;
				map.drawImage(sector, (int) XStartPos, (int) YStartPos, null);
			}
		}

		// draw barcodes
		if (drawBarcodes) {
			for (Barcode code : maze.getBarcodes()) {
				addBarcode(code.getX(), code.getY(), code.getCode(), code.getDirection());

				int x = code.getX();
				int y = walls.length - 2 - code.getY();

				char[] bcode = new char[code.getCode().length()];
				for (int i = 0; i < bcode.length; i++) {
					bcode[i] = code.getCode().charAt(i);
				}

				if (code.getDirection() == 1)
					drawBarcode(map, x, y, bcode, true);
				else
					drawBarcode(map, x, y, bcode, false);

			}
		}

		// draw walls and fog
		map.setColor(new Color(90, 43, 0));
		int YStart = 0;
		int XStart = 0;
		boolean xskipped = false;
		char[][] data = mazeToDraw;

		// draw walls and fog on top row
		for (int x = 0; x < (2 * size + 1); x++) {
			int yy = data.length - 1;
			if (!xskipped && !(((x - 1) / 2) < maxX)) {
				XStart += 40;
				xskipped = true;
			}
			if (data[yy][x] == 1) {
				if (x % 2 != 0) {
					if (((x - 1) / 2) < maxX) {
						map.drawLine(XStart, YStart, XStart + 40, YStart);
						XStart += 40;
					}
				} else {
					if (!(((x - 1) / 2) < maxX)) {
						map.drawLine(XStart, YStart, XStart + 40, YStart);
						XStart += 40;
					}
				}
			} else if (data[yy][x] == 0) {
				;
				if (x % 2 != 0) {
					if (((x - 1) / 2) < maxX) {
						fogfwar.drawImage(fog, XStart, YStart - 20, null);
						XStart += 40;
					}
				} else {
					if (!(((x - 1) / 2) < maxX)) {
						fogfwar.drawImage(fog, XStart, YStart - 20, null);
						XStart += 40;
					}
				}
			} else {
				if (x % 2 != 0) {
					if (((x - 1) / 2) < maxX)
						XStart += 40;
				} else {
					if (!(((x - 1) / 2) < maxX))
						XStart += 40;
				}
			}
		}
		XStart = 0;
		YStart = 40;
		xskipped = false;

		// draw all the other walls and fog
		for (int y = 0; y < size + 1; y++) {
			for (int x = 0; x < (2 * size + 1); x++) {
				if (!xskipped && !(((x - 1) / 2) < maxX)) {
					XStart += 40;
					xskipped = true;
				}
				if (data[y][x] == 1) {
					if (x % 2 != 0) {
						if (((x - 1) / 2) < maxX) {
							map.drawLine(XStart, YStart, XStart + 40, YStart);
							XStart += 40;
						} else {
							map.drawLine(XStart, YStart, XStart, YStart - 40);
						}
					} else {
						if (((x - 1) / 2) < maxX) {
							map.drawLine(XStart, YStart, XStart, YStart - 40);
						} else {
							map.drawLine(XStart, YStart, XStart + 40, YStart);
							XStart += 40;
						}
					}
				} else if (data[y][x] == 0) {
					;
					if (x % 2 != 0) {
						if (((x - 1) / 2) < maxX) {
							fogfwar.drawImage(fog, XStart, YStart - 20, null);
							XStart += 40;
						} else {
							fogfwar.drawImage(fog, XStart - 20, YStart - 40, null);
						}
					} else {
						if (((x - 1) / 2) < maxX) {
							fogfwar.drawImage(fog, XStart - 20, YStart - 40, null);
						} else {
							fogfwar.drawImage(fog, XStart, YStart - 20, null);
							XStart += 40;
						}
					}
				} else {
					if (x % 2 != 0) {
						if (((x - 1) / 2) < maxX) {
							XStart += 40;
						}
					} else {
						if (!(((x - 1) / 2) < maxX)) {
							XStart += 40;
						}
					}
				}
			}
			YStart += 40;
			XStart = 0;
			xskipped = false;
		}
		YStart = 40;
		XStart = temp.getWidth();
		// draw the right column
		for (int y = 0; y < size + 1; y++) {
			if (data[y][0] == 1) {
				map.drawLine(XStart, YStart, XStart, YStart - 40);
			} else if (data[y][0] == 0) {
				;
				fogfwar.drawImage(fog, XStart - 20, YStart - 40, null);
			}
			YStart += 40;
		}

		double x = (ghostsData.get(toDraw - 1).getLocation().x / 40);
		double y = (ghostsData.get(toDraw - 1).getLocation().y / 40);

		// x = Math.abs(x%(data.length-1));
		x = positivemodulo(x, size + 1);

		y = positivemodulo(y, size + 1);
		y = size + 1 - y;

		int angle = ghostsData.get(toDraw - 1).getRotation();

		BufferedImage rotatedbot = scale(rotate(robot, angle), 0.75);
		map.drawImage(rotatedbot, (int) ((x) * 40) - rotatedbot.getWidth() / 2, (int) ((y) * 40) - rotatedbot.getHeight() / 2, null);
		map.drawString(ghostsData.get(toDraw - 1).getName(), (int) ((x) * 40) - 20, (int) ((y) * 40) - 20);

		fogfwar.dispose();
		// map.drawImage(fogofwar, 0,0, null);
		// map.drawImage(scale(scale(fogofwar,1/8),8), 0,0, null);
		map.drawImage(fogofwar, 0, 0, null);
		map.dispose();
		BufferedImage temp2 = new BufferedImage((maxX - minX) * 40, (maxY - minY) * 40, BufferedImage.TYPE_INT_ARGB);
		Graphics gr = temp2.getGraphics();

		int d_width = temp2.getWidth();
		int d_height = temp2.getHeight();
		int d_xborder = -minX * 40;
		int d_yborder = maxY * 40;

		int s_width = temp.getWidth();
		int s_height = temp.getHeight();
		int s_xmax = maxX * 40;
		int s_ymax = s_height - maxY * 40;
		int s_xminwrap = s_width + minX * 40;
		int s_yminwrap = -minY * 40;

		// x-y-
		gr.drawImage(temp, 0, d_yborder, d_xborder, d_height, s_xminwrap, 0, s_width, s_yminwrap, null);
		// x+y+
		gr.drawImage(temp, d_xborder, 0, d_width, d_yborder, 0, s_ymax, s_xmax, s_height, null);
		// x-y+
		gr.drawImage(temp, 0, 0, d_xborder, d_yborder, s_xminwrap, s_ymax, s_width, s_height, null);
		// x+y-
		gr.drawImage(temp, d_xborder, d_yborder, d_width, d_height, 0, 0, s_xmax, s_yminwrap, null);
		gr.dispose();

		gr = m.getGraphics();
		gr.setColor(new Color(50, 50, 50));
		gr.drawImage(temp2, 0, 0, null);
		gr.fillRect(temp2.getWidth(), 0, m.getWidth(), m.getHeight());
		gr.fillRect(0, temp2.getHeight(), m.getWidth(), m.getHeight());
		gr.dispose();
		
		
		//DRAW DIFFUSION MAP
		if(diffusionMaze!=null && drawDiffusion) {
			gr = m.getGraphics();
			int iiy = 0;
			int iix = 0;
			for(int iy = maxY-1; iy>=minY; iy--) {
				for(int ix = minX; ix<=maxX-1; ix++) {
					int diffusionValue = diffusionMaze.read(ix, iy, Direction.NORTH, true) - diffusionMaze.read(ix, iy, Direction.EAST, true) - (65535-16);
					diffusionValue *= 16;
					diffusionValue = Math.max(Math.min(255, diffusionValue),0);
					gr.setColor(new Color(diffusionValue, diffusionValue, diffusionValue, 128));
					gr.fillRect(iix*40, iiy*40, 40, 40);
					iix++;
				}
				iiy++;
				iix=0;
			}
			gr.dispose();
		}
		
		return m;
	}

	public void addBarcode(int xPos, int yPos, String BC, int direction) {

		int x = xPos;
		int y = walls.length - 2 - yPos;

		char[] code = new char[BC.length()];
		for (int i = 0; i < code.length; i++) {
			code[i] = BC.charAt(i);
		}
		if (map == null) {
			map = new BufferedImage(40 * walls.length, 40 * walls[0].length / 2, BufferedImage.TYPE_INT_ARGB);
			drawMazeImage(map, walls, true);

		}
		Graphics gr = map.getGraphics();

		if (direction == 1)
			drawBarcode(gr, x, y, code, true);
		else
			drawBarcode(gr, x, y, code, false);

		gr.dispose();
	}

	/**
	 * draw method to paint to an image in memory in stead of to the screen
	 */
	private void draw() {
		if (!drawing) {
			drawing = true;

			drawMazeToBuffer();

			repaint();
			drawing = false;

		}

	}

	/**
	 * the drawing code
	 */
	private void drawMazeToBuffer() {
		MazePanelBuffer = new BufferedImage(800, 600, BufferedImage.TYPE_INT_ARGB);
		Graphics2D buffer = (Graphics2D) MazePanelBuffer.getGraphics();
		buffer.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		buffer.drawImage(background, 0, 0, 300, 300, 0, 0, 350, 350, null);
		buffer.drawImage(background, 300, 0, 600, 300, 0, 0, 350, 350, null);
		buffer.drawImage(background, 0, 300, 300, 600, 0, 0, 350, 350, null);
		buffer.drawImage(background, 300, 300, 600, 600, 0, 0, 350, 350, null);
		if (toDraw == 0) {
			drawMainMap(buffer);
		} else {
			Maze maze = ghosts.get(toDraw - 1).getMaze();
			int width = maze.getSize();
			// System.out.println("maze size: " + maze.getRawData().length +
			// " x " + maze.getRawData()[0].length);
			BufferedImage botMap = new BufferedImage(40 * width, 40 * width, BufferedImage.TYPE_INT_ARGB);
			Maze diffusionMaze = null;
			if(ghosts.get(toDraw - 1).getCurrentStrategy() instanceof PacmanFinder) diffusionMaze = ((PacmanFinder)ghosts.get(toDraw - 1).getCurrentStrategy()).collaborativeDiffusionMaze;
			botMap = drawGhostMazeImage(botMap, maze, false,diffusionMaze);
			buffer.drawImage(botMap, 0, 0, 600, 600, null);// (int)(0 +
															// 40*xOffset),
															// (int)(0+40*yOffset),
															// 600+(int)(40*xOffset),
															// 600+(int)(40*yOffset),
															// null);
		}
		if (map != null) {
			Graphics mpg = map.getGraphics();
			int xx;
			int yx;
			Color[] playerColors = { new Color(255, 255, 0), new Color(0, 255, 255), new Color(0, 80, 0), new Color(255, 0, 0) };
			int[][] lineOffsets = new int[4][4];
			lineOffsets[0][0] = 2;	lineOffsets[0][1] = 2;	lineOffsets[0][2] = 3;	lineOffsets[0][3] = 3;
			lineOffsets[1][0] = -1;	lineOffsets[1][1] = 1;	lineOffsets[1][2] = -2;	lineOffsets[1][3] = 2;
			lineOffsets[2][0] = 1;	lineOffsets[2][1] = -1;	lineOffsets[2][2] = 2;	lineOffsets[2][3] = -2;
			lineOffsets[3][0] = -2;	lineOffsets[3][1] = -2;	lineOffsets[3][2] = -3;	lineOffsets[3][3] = -3;
			try {
				for (int i = 0; i < game.getGhosts().length; i++) { //Er zijn dus eigenlijk 4 ghosts in de game
					mpg.setColor(playerColors[i]);
					xx = (int) ((simulator.getGhostLocations().get(ghosts.get(i)).x)); //TODO: ghosts is nu geinitialiseerd als de lijst van ghosts in de simulator
					yx = (int) ((simulator.getGhostLocations().get(ghosts.get(i)).y)); //Dit moet de lijst met ghosts van de game worden, maar die hebben geen goede locatie..
					yx = (walls.length - 1) * 40 - yx;
					mpg.drawLine(xx + lineOffsets[i][0], yx + lineOffsets[i][1], xx + lineOffsets[i][2], yx + lineOffsets[i][3]);
				}
			} catch (Exception e) {
			}
			mpg.dispose();
			// System.out.println("draw trail");
		}

		//
		// draw the panels background for the data screens
		drawPanels(buffer);

		// draw the values of the data, to be switched with real data from the
		// simulator tommorow
		drawData(buffer);

		// draw the graphs of the lightsensor
		drawGraphs(buffer);

		buffer.dispose();
		MazePanel = MazePanelBuffer;
	}

	private void drawGraphs(Graphics2D buffer) {
		buffer.setColor(new Color(255, 128, 0));
		for (int i = 1; i < lights[0].length; i++) {
			buffer.drawLine(599 + (4 * i), 120 - lights[0][i - 1], 600 + (4 * i), 120 - lights[0][i]);
			buffer.drawLine(599 + (4 * i), 270 - lights[1][i - 1], 600 + (4 * i), 270 - lights[1][i]);
			buffer.drawLine(599 + (4 * i), 420 - lights[2][i - 1], 600 + (4 * i), 420 - lights[2][i]);
			buffer.drawLine(599 + (4 * i), 570 - lights[3][i - 1], 600 + (4 * i), 570 - lights[3][i]);
		}
	}

	private void drawData(Graphics2D buffer) {
		for (int i = 0; i < 4; i++) {
			try {
				buffer.setColor(new Color(255, 128, 0));

				buffer.drawString("UltraSonic: " + ghostsData.get(i).getUltrasonic(), 605, 40 + i * 150);

				// System.out.println("UltraSonic: " +
				// ghostsData.get(i).ultrasonic);
				// buffer.drawString("IR: " +
				// simulator.SimulateInfraredSensor(ghosts.get(i)),605,60 +
				// i*150);
				buffer.drawString("IR: " + (ghostsData.get(i).getIr()), 605, 60 + i * 150);
				buffer.drawString("Light: " + ghostsData.get(i).getLight(), 605, 80 + i * 150);
				buffer.drawString("Name: Ghost-" + (ghostsData.get(i).getName()), 605, 20 + i * 150);
				buffer.setColor(new Color(1f, 0.5f, 0f, 0.6f));
				buffer.drawLine(600, 120 + i * 150, 800, 120 + i * 150);
				buffer.drawLine(600, 140 + i * 150, 800, 140 + i * 150);
				buffer.drawLine(600, 100 + i * 150, 800, 100 + i * 150);
			} catch (Exception e) {
				buffer.setColor(new Color(255, 128, 0));
				buffer.drawString("Name: Not connected" + (i + 1), 605, 20 + i * 150);
				buffer.drawString("UltraSonic: NA", 605, 40 + i * 150);
				buffer.drawString("IR: NA", 605, 60 + i * 150);
				buffer.drawString("Light: NA", 605, 80 + i * 150);
				buffer.setColor(new Color(1f, 0.5f, 0f, 0.6f));
				buffer.drawLine(600, 120 + i * 150, 800, 120 + i * 150);
				buffer.drawLine(600, 140 + i * 150, 800, 140 + i * 150);
				buffer.drawLine(600, 100 + i * 150, 800, 100 + i * 150);
			}
		}
	}

	private void drawPanels(Graphics2D buffer) {
		buffer.drawImage(blackScreen, 600, 0, 800, 150, 0, 0, blackScreen.getWidth(), blackScreen.getHeight(), null);
		buffer.drawImage(blackScreen, 600, 150, 800, 300, 0, 0, blackScreen.getWidth(), blackScreen.getHeight(), null);
		buffer.drawImage(blackScreen, 600, 300, 800, 450, 0, 0, blackScreen.getWidth(), blackScreen.getHeight(), null);
		buffer.drawImage(blackScreen, 600, 450, 800, 600, 0, 0, blackScreen.getWidth(), blackScreen.getHeight(), null);
		buffer.setColor(new Color(255, 128, 0));
	}

	private void drawMainMap(Graphics2D buffer) {
		buffer.drawImage(map, 0, 0, 600, 600, (int) (0 + 40 * xOffset), (int) (0 + 40 * yOffset), 600 + (int) (40 * xOffset), 600 + (int) (40 * yOffset), null);

		if (drawColormap) {

			buffer.drawImage(colormap, 0, 0, 600, 600, (int) (0 + 40 * xOffset), (int) (0 + 40 * yOffset), 600 + (int) (40 * xOffset), 600 + (int) (40 * yOffset), null);
			// buffer.drawImage(color.getTileMap()[0][0],0,0,null);
		}

		buffer.setColor(new Color(54, 125, 217));
		// map = new BufferedImage(40*walls.length, 40*walls[0].length/2,
		// BufferedImage.TYPE_INT_ARGB);
		// buffer.drawImage(rotate(robot,angle),(int)((2-xOffset)*40),(int)((5-yOffset)*40),null);
		// buffer.drawString("Ghost-"+
		// 1,(int)((2-xOffset)*40),(int)((5-yOffset)*40));

		// draw the ghosts in the simulator
		
		try {
			for (DataCollector bot : ghostsData) {
				double x = (simulator.getGhostLocations().get(bot.getGhost()).x / 40);
				double y = (simulator.getGhostLocations().get(bot.getGhost()).y / 40);
				int angle = simulator.getGhostRotations().get(bot.getGhost());
				int ultrasonicAngle = bot.getUltrasonicRotation();
				int distance = bot.getUltrasonic();
				if (!((int) ((x - xOffset) * 40) >= 600)) {
					y = walls.length - 1 - y;

					// draw ultrasonic
					BufferedImage ultra = new BufferedImage(distance * 2 + 1, distance * 2 + 1, BufferedImage.TYPE_INT_ARGB);
					Graphics graphics = ultra.getGraphics();
					graphics.drawImage(ultrasonic, distance, distance, null);
					graphics.dispose();
					buffer.drawImage(rotate(ultra, angle + ultrasonicAngle), (int) ((x - xOffset) * 40) - distance - (int) (Math.sin(Math.toRadians(angle)) * 10), (int) ((y - yOffset) * 40)
							- distance - (int) (Math.cos(Math.toRadians(angle)) * 10), null);

					// draw robot in simulator
					BufferedImage rotatedbot = scale(rotate(robot, angle), 0.75);
					buffer.drawImage(rotatedbot, (int) ((x - xOffset) * 40) - rotatedbot.getWidth() / 2, (int) ((y - yOffset) * 40) - rotatedbot.getHeight() / 2, null);
					buffer.drawString("Ghost-" + bot.getName(), (int) ((x - xOffset) * 40) - 20, (int) ((y - yOffset) * 40) - 20);

				}
				
			}
			
			//draw other robots from game
			for (Ghost ghost : game.getGhosts()) {
				//System.out.println("ghost "+ ghost.getName() + " game");
				if (!ghosts.get(0).equals(ghost)) {// if this ghost is not in the simulator running on this pc
				
				
				
				
//					if(!ghosts.get(0).getMazeOnName(ghost.getName()).getCoordinateOfLocation(ghost.getLocation(), false).equals(ghosts.get(0).getMazeOnName(ghost.getName()).getCoordinateOfLocation(ghost.getLocation(), true)))
//					{
//					
//					Location ghostLocation = new Location(game.getGhostPositions().get(ghost).x*40 +1,game.getGhostPositions().get(ghost).y*40 +1);
//					Coordinate newco = ghosts.get(0).getMazeOnName(ghost.getName()).getCoordinateOfLocation(ghostLocation, false);
//	
//					double x = newco.x;
//					double y = newco.y;
//					if (!((int) ((x - xOffset) * 40) >= 600)) 
//						y = walls.length - 1 - y;
//					
//					int angle = 0;
//					int ultrasonicAngle = 0;
//					int distance = 0;
//					BufferedImage rotatedbot = scale(rotate(robot, angle), 0.75);
//					buffer.drawImage(rotatedbot, (int) ((x - xOffset+0.5) * 40) - rotatedbot.getWidth() / 2, (int) ((y - yOffset+0.5) * 40) - rotatedbot.getHeight() / 2, null);
//					buffer.setColor(Color.RED);
//					buffer.drawString("Fake-" + ghost.getName(), (int) ((x - xOffset+0.5) * 40) - 20, (int) ((y - yOffset+0.5) * 40) - 20);
//					}
				
				}
			}
			
		} catch (Exception e) {

		}
		;

		// draw pacman at (x,y)
		try {
			x = simulator.getPacman().getLocation().x / 40;
			y = simulator.getPacman().getLocation().y / 40;
		} catch (Exception e) {
		}
		;
		if (!((int) ((x - xOffset) * 40) >= 600)) {

			y = walls.length - 1 - y;
			buffer.drawImage(pacman, (int) ((x - xOffset) * 40) - 20, (int) ((y - yOffset) * 40) - 20, (int) ((x - xOffset) * 40) + 40 - 20, (int) ((y - yOffset) * 40) + 40 - 20, 0, 0,
					pacman.getWidth(), pacman.getHeight(), null);
		}
	}

	@Override
	public void update(Graphics g) {
		paint(g);
	}

	@Override
	public void paint(Graphics g) {

		if (initiated) {
			if (map == null) {
				map = new BufferedImage(40 * walls.length, 40 * walls[0].length / 2, BufferedImage.TYPE_INT_ARGB);
				drawMazeImage(map, walls, true);
				draw();
			}
			if (colormap == null) {
				if (color != null) {
					colormap = new BufferedImage(40 * color.getTileMap().length, 40 * color.getTileMap()[0].length, BufferedImage.TYPE_INT_RGB);
					drawColorMap(colormap.getGraphics());
					draw();

				}
			}

			g.drawImage(MazePanel, 0, 0, getWidth(), getHeight(), null);
		}
		// g.drawImage(map, 0, 0, 600, 600, (int)(0 + 40*xOffset),
		// (int)(0+40*yOffset), 600+(int)(40*xOffset), 600+(int)(40*yOffset),
		// null);
		// drawBarcode example
		// int i = 5;
		// int j = 6;
		// char[] code = {0,1,1,0,1,0,1,1};
		// drawBarcode(g, i, j, code, true);
		// drawBarcode(g, 12, 2, code, false);

		// drawpacman

		// Graphics2D bot = (Graphics2D) robot.getGraphics();
		// bot.rotate(1.2, robot.getWidth(), robot.getHeight());

		// g.drawImage(robotrotated,120,120,null);
		// bot.dispose();
		// if (!((int)((x-xOffset)*40)>=600-40))
		// g.drawImage(pacman,(int)((x-xOffset)*40),(int)((y-yOffset)*40),(int)((x-xOffset)*40)
		// +40,(int)((y-yOffset)*40)+40,0,0,pacman.getWidth(),pacman.getHeight(),null);

	}

	/**
	 * Draws a barcode at a given position on an image, draws from left to right
	 * or up to down.
	 * 
	 * @param g
	 *            |The graphics to paint on
	 * @param x
	 *            |The X position of the tile where to draw the barcode
	 * @param y
	 *            |The Y position of the tile where to draw the barcode
	 * @param code
	 *            |The actual barcode to draw
	 * @param hor
	 *            |Boolean to indicate if the code is horizontal or vertical
	 */
	private void drawBarcode(Graphics g, int x, int y, char[] code, boolean hor) {

		if (hor) {
			for (int i = 0; i < 2 * code.length; i += 2) {

				if (code[i / 2] == '0')
					g.setColor(new Color(0, 0, 0));
				else
					g.setColor(new Color(255, 255, 255));

				g.drawLine(x * 40, (y * 40) + code.length + 3 + i, (x + 1) * 40, (y * 40) + code.length + 3 + i);
				g.drawLine(x * 40, (y * 40) + code.length + 3 + i + 1, (x + 1) * 40, (y * 40) + code.length + 3 + i + 1);
			}

		} else {
			for (int i = 0; i < 2 * code.length; i += 2) {
				if (code[i / 2] == '0')
					g.setColor(new Color(0, 0, 0));
				else
					g.setColor(new Color(255, 255, 255));

				g.drawLine(x * 40 + code.length + i + 3, (y * 40), (x) * 40 + code.length + i + 3, ((y + 1) * 40));
				g.drawLine(x * 40 + code.length + i + 1 + 3, (y * 40), (x) * 40 + code.length + i + 1 + 3, ((y + 1) * 40));
			}
		}
	}

	public static void main(String[] args) throws IOException {
		char[][] walls = { // index with maze[-y][x]
		{ 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 },
				{ 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 2, 1 },
				{ 1, 2, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1 },
				{ 1, 2, 1, 2, 1, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1 },
				{ 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1 },
				{ 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1 },
				{ 1, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2, 1, 2, 2, 1 },
				{ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1 },
				{ 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 2, 2, 2, 2, 1 },
				{ 1, 2, 1, 2, 2, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
				{ 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1 },
				{ 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
				{ 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
				{ 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1 },
				{ 1, 2, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 1 },
				{ 1, 2, 2, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1 },
				{ 1, 1, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 1, 2, 1, 2, 2, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1 },
				{ 1, 2, 2, 1, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1 },
				{ 1, 1, 2, 2, 1, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1 },
				{ 1, 2, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1, 1, 2, 2, 2, 1, 2, 1 },
				{ 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1 } };
		Maze maze = new Maze(walls);
		ColorMap map = new ColorMap();
		maze.addBarcode(10, 15, "01101001", 0);
		maze.addBarcode(4, 12, "01111000", 1);
		maze.addBarcode(6, 10, "01010110", 0);
		maze.addBarcode(0, 12, "01110110", 1);
		maze.addBarcode(14, 6, "01011100", 1);

		maze.addBarcode(3, 6, "01110010", 0);
		maze.addBarcode(11, 9, "01111110", 1);
		maze.addBarcode(12, 17, "01001000", 0);
		map.setupMap(maze, walls.length - 1, walls.length - 1);

		Game game = new Game();
		Channel channel = game.getChannel();
		Ghost ghost = null;
		Ghost ghost2 = null;
		Ghost ghost3 = null;
		Ghost ghost4 = null;
		// Ghost ghost;
		try {
			// ghost = new Ghost(new Location(20,20), 0,"jan");
			ghost = new Ghost(new Location(20, 20), 0, "name1",game);
			ghost2 = new Ghost(new Location(20, 20), 0, "name2",game);
			ghost3 = new Ghost(new Location(20, 20), 0, "name3",game);
			ghost4 = new Ghost(new Location(20, 20), 0, "name4",game);
			Ghost[] ghosts = { ghost, ghost2, ghost3, ghost4 };
			game.setGhosts(ghosts);
		} catch (IOException e) {
		
			e.printStackTrace();
		}

		Simulator simulation = new Simulator(maze, map);
		simulation.addGhost(ghost, new Location(140, 340), 0);
		simulation.addGhost(ghost2, new Location(460, 780), 0);
		simulation.addGhost(ghost3, new Location(180, 260), 90);
		simulation.addGhost(ghost4, new Location(460, 420), -90);
		ghost.setSimulator(simulation);
		ghost2.setSimulator(simulation);
		ghost3.setSimulator(simulation);
		ghost4.setSimulator(simulation);
		ghost.setStrategy(new Explorer(ghost.getMaze()));
		ghost2.setStrategy(new Explorer(ghost2.getMaze()));
		ghost3.setStrategy(new Explorer(ghost3.getMaze()));
		ghost4.setStrategy(new Explorer(ghost4.getMaze()));
		simulation.speed = 600;
		Pacman pacman = new Pacman(new Location(420, 440));
		simulation.setPacman(pacman);
		GuiTest gui = new GuiTest(simulation,game);
		 
		Thread t1 = new Thread(new GhostRunner(ghost));
		Thread t2 = new Thread(new GhostRunner(ghost2));
		Thread t3 = new Thread(new GhostRunner(ghost3));
		Thread t4 = new Thread(new GhostRunner(ghost4));
		t1.start();
		t2.start();
		
		t3.start();
		t4.start();

	}

	
	/**
	 * Rotates an image at a certain angle in degrees
	 * 
	 * @param img
	 *            |The Image to rotate
	 * @param angle
	 *            |The angle to rotate, counterclockwise
	 * @return |A copy of the image rotated;
	 */
	public static BufferedImage rotate(BufferedImage img, int angle) {
		angle = -angle;
		int w = img.getWidth();
		int h = img.getHeight();
		BufferedImage dimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = dimg.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
		g.rotate(Math.toRadians(angle), w / 2, h / 2);

		g.drawImage(img, null, 0, 0);
		return dimg;
	}

	public static BufferedImage scale(BufferedImage img, double factor) {

		int w = (int) (img.getWidth() * factor);
		int h = (int) (img.getHeight() * factor);
		BufferedImage dimg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = dimg.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		g.scale(factor, factor);

		g.drawImage(img, null, 0, 0);
		return dimg;
	}

	private double positivemodulo(double val, double mod) {
		mod = Math.abs(mod); // no negative modulo allowed
		mod = (mod == 0) ? 1 : mod; // no zero modulo either
		while (val < 0)
			val += mod; // increase val until we're sure it's positive
		return val % mod;
	}

	private class MyDispatcher implements KeyEventDispatcher {
		@Override
		public boolean dispatchKeyEvent(KeyEvent e) {

			if (e.getID() == KeyEvent.KEY_PRESSED) {
				int keyCode = e.getKeyCode();

				switch (keyCode) {
				case KeyEvent.VK_Z:
					setPosition(0, -0.1);
					// draw();
					break;
				case KeyEvent.VK_Q:
					setPosition(-0.1, 0);
					// draw();
					break;
				case KeyEvent.VK_D:
					setPosition(0.1, 0);
					// draw();
					break;
				case KeyEvent.VK_S:
					setPosition(0, +0.1);
					// draw();
					break;
				case KeyEvent.VK_ENTER:
					drawColormap = !drawColormap;

					break;
				case KeyEvent.VK_NUMPAD5:

					try {
						simulator.SimulateTravel(ghosts.get(selectedghost), -100);
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
					break;
				case KeyEvent.VK_NUMPAD8:

					try {
						simulator.SimulateTravel(ghosts.get(selectedghost), 100);
					} catch (IOException e1) {
						
						e1.printStackTrace();
					}
					break;
				case KeyEvent.VK_1:
					selectedghost = 0;
					toDraw = 1;
					break;
				case KeyEvent.VK_2:
					selectedghost = 1;
					toDraw = 2;
					break;
				case KeyEvent.VK_3:
					selectedghost = 2;
					toDraw = 3;
					break;
				case KeyEvent.VK_4:
					selectedghost = 3;
					toDraw = 4;
					break;
				case KeyEvent.VK_0:
					toDraw = 0;
					break;
				case KeyEvent.VK_SPACE:
					drawDiffusion = !drawDiffusion;
					break;
				}
			} else if (e.getID() == KeyEvent.KEY_RELEASED) {

				int keyCode = e.getKeyCode();
				switch (keyCode) {
				case KeyEvent.VK_UP:
					// if (yOffset > 0){
					yOffset -= 1;// }
					// draw();
					break;
				case KeyEvent.VK_DOWN:
					// if (yOffset < (walls.length) - 16){
					yOffset += 1;// }

					// draw();
					break;
				case KeyEvent.VK_LEFT:
					// if (xOffset >0 ){
					xOffset -= 1;// }

					// draw();
					break;
				case KeyEvent.VK_RIGHT:
					// if (xOffset < (walls[0].length/2) - 15){
					xOffset += 1;// }

					// draw();
					break;
				case KeyEvent.VK_NUMPAD4:

					simulator.SimulateRotation(ghosts.get(selectedghost), 10);

					break;
				case KeyEvent.VK_NUMPAD6:

					simulator.SimulateRotation(ghosts.get(selectedghost), -10);

					break;
				}
			}
			return false;
		}
	}

}
